package PIServer;

//import org.apache.mina.common.IdleStatus;
//import org.apache.mina.common.IoHandlerAdapter;
//import org.apache.mina.common.IoSession;
//import org.apache.mina.common.TransportType;

import Bridging.BridgeService;
import CardGame.GameTable;
import CardGame.RoomManager;
import Service.LeaveUserObj;
import Shared.MapManager;
import Service.ServerService;
import Message.BeanConstant;
import Constants.Service;
import DataManager.DbManager;
import DataManager.MySQLCommonDataProvider;
import DataManager.VCoreUserDataProvider;
import Service.FarmService;
import Service.FishingService;
import Service.ParadiseService;
import CardGame.TienLenService;
import Service.UserService;
import Shared.GameMapObj;
import Utility.CommonUtils;
import java.util.concurrent.ConcurrentHashMap;
import java.util.*;
import Message.Message;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.mina.core.service.IoHandlerAdapter;
import org.apache.mina.core.session.IdleStatus;
import org.apache.mina.core.session.IoSession;
import Message.SerializerHelper;

public class VMSProtocolHandler extends IoHandlerAdapter {

    public static final int MAX_GAME_TABLE = 2000;
    public static final int MAX_GAME_TABLE_PER_ROOM = 30;
    public static final Logger log = LoggerFactory.getLogger(VMSProtocolHandler.class.getName());
//    public static Set<IoSession> sessions = Collections.synchronizedSet(new HashSet<IoSession>());
    public static ConcurrentHashMap<Integer, IoSession> clientSessions =
            new ConcurrentHashMap(new HashMap<Integer, IoSession>());
    public static ConcurrentHashMap<String, LeaveUserObj> leaveUserQueue = new ConcurrentHashMap(new HashMap<String, LeaveUserObj>());
    public static TienLenService serviceTienLen = null;
    public static ParadiseService serviceParadise = null;
//    public static BinhService serviceBinh = null;
//    public static DominoService serviceDomino = null;
//    public static BattleshipService serviceBattleship = null;
//    public static PokerService servicePoker = null;
//    public static ChatService serviceChat = null;
    public static FarmService farmService = null;
    public static FishingService fishingService = null;

    static void cleanUp() {
        // update logout time for all users before shutdown

//        synchronized (VMSProtocolHandler.sessions) {
        Iterator<IoSession> iter = VMSProtocolHandler.clientSessions.values().iterator();
        while (iter.hasNext()) {
            IoSession s = (IoSession) iter.next();
            try {
                int userID = (Integer) s.getAttribute("userid");
                VCoreUserDataProvider.getInstance().UpdateActiontime(userID, 1);
            } catch (Exception ex) {
                log.error("Clean up user", ex);
            }
        }
//        }
    }

    public VMSProtocolHandler() {
        serviceTienLen = new TienLenService();
        serviceParadise = new ParadiseService();
        farmService = new FarmService();
        fishingService = new FishingService();
//        serviceBinh = new BinhService();
//        serviceDomino = new DominoService();
//        serviceBattleship = new BattleshipService();
//        servicePoker = new PokerService();
//        serviceChat = new ChatService();
        //updateUserID();
    }

    @Override
    public void sessionCreated(IoSession session) {
        //session.setIdleTime(IdleStatus.BOTH_IDLE, VMServer.idleTimeBeforeDisconnect);
        session.getConfig().setBothIdleTime(Main.idleTimeBeforeDisconnect);
    }

    @Override
    public void sessionIdle(IoSession session, IdleStatus status) {
        try {
//            log.info("*** IDLE #" + session.getIdleCount(IdleStatus.BOTH_IDLE) + " ***");
            // Disconnect them
            //sessionWrite(session, "SESSION_TIMED_OUT");
            String user = (String) session.getAttribute("user");
            if (user != null) {
                log.info("User [{}] disconnected from being idled ***", user);

            }
            session.close(false);
        } catch (Exception ex) {
            //log.error(ex.getMessage());
            log.error("sessionIdle", ex);
        }
    }

    @Override
    public void exceptionCaught(IoSession session, Throwable cause) {
        try {
            if (!(cause instanceof java.io.IOException)) {
                log.error("exceptionCaught", cause);
            }
//            cause.printStackTrace();
            session.close(false);
        } catch (Exception ex) {
            //log.error(ex.getMessage());
            log.error("exceptionCaught", ex);
        }
    }

    private boolean connectionLimitHit(IoSession session) {
        if (getOnlineUserCount() > Main.maximumConnections) {
            sessionWrite(session, "CONNECTION_LIMIT_HIT");
            session.close(false);
            log.info("Connection limit hit (Maximum connections allowed is: " + Main.maximumConnections + ")");
            return true;
        } else {
            return false;
        }
    }

    @Override
    public void messageReceived(IoSession session, Object message) {
        String[] result = null;
        //String theCommand = "";
        //String incomingMessage = "";
        String currentRoom = "";

        Message clientMsg = (Message) message;
        int actionCommand = clientMsg.getActionCommand();
        int svcType = clientMsg.getServiceType();

        String userName = (String) session.getAttribute("user");
        int userID = CommonUtils.getInt(session.getAttribute("userid"));
        currentRoom = (String) session.getAttribute("room");

        log.debug(String.format("[%d] [%s/%d] Received: %d",
                session.getId(),
                userName, userID,
                clientMsg.getActionCommand()));

        try {
//            if (connectionLimitHit(session))
//            {
//                return;
//            }

//            incomingMessage = (String) message;
//            incomingMessage = incomingMessage.trim();
//
//            if (incomingMessage.length() <= 0)
//            {
//                session.close(false);
//                return;
//            }
//
//            if (incomingMessage.equalsIgnoreCase("<policy-file-request/>"))
//            {
//                //log.info("*** Sending policy file ***");
//                session.write(VMServer.policyFile);
//                session.close(false);
//                return;
//            }

            //log.info("Original Message length: " + incomingMessage.length());
            //incomingMessage = Base64Coder.decodeString(incomingMessage);
            //log.info("Incoming Message length: " + incomingMessage.length());

            //log.info("getLastIoTime: " + session.getLastIoTime());
            //log.info("getServiceAddress: " + session.getServiceAddress());

//            if (incomingMessage.contains("~"))
//            {
//                result = incomingMessage.split("~");
//                theCommand = result[0].trim();
//            }
//            else
//            {
//                theCommand = incomingMessage;
//            }

            //ActionCommand command = ActionCommand.valueOf(theCommand);

//            if (actionCommand==ActionCommand.LOGIN) {
//                Message msg1 = new Message(BeanConstant.LOGIN_SUCCESS, Constant.Service.SYSTEM_SERVICE);
//
//                SerializerHelper.writeString("username", msg1);
//                SerializerHelper.writeString("password", msg1);
//                try {
//                    session.write(msg1);
//                } catch (Exception ex) {
//                    ex.printStackTrace();
//                }
//               // return;
//            }

            // if (svcType==Constant.Service.SYSTEM_SERVICE) {
            switch (actionCommand) {

                case 999:
                    String str1 = SerializerHelper.readString(clientMsg);
                    String str2 = SerializerHelper.readString(clientMsg);
                    log.info("Message content: str1=" + str1 + ", str2=" + str2);

                    Message msg1 = new Message(999, 1);
                    SerializerHelper.writeString("Bonjour", msg1);
                    SerializerHelper.writeString("Woman is from Venus", msg1);
                    session.write(msg1);
                    return;
            }
            // }
//

//
            if (userName == null) {
                if ((svcType == Service.CHAT_SERVICE)
                        && ((actionCommand == BeanConstant.SYS_LOGIN_REQUEST)
                        || (actionCommand == BeanConstant.SYS_REGISTER_REQUEST)
                        || (actionCommand == BeanConstant.SYS_CHECK_VERSION)
                        || (actionCommand == BeanConstant.SYS_GET_SMS_SYNTAX))) {
                    // OK, empty username is acceptable for login/register
                } else {
                    if (session!=null) {
                        log.error("Access denied  - user {} (sesssion {})", userName, session.getId());
                        session.close(false);
                    }
                    return;
                }
            }


            switch (svcType) {
                case Service.GAME_CARD_SERVICE:
//                    serviceTienLen.processCommand(session, userID, userName, clientMsg);
                    BridgeService.processCommand(session, userID, userName, clientMsg);
                    break;

                case Service.DATING_DATING_SERVICE:
                    UserService.ProcessCommand(session, userID, userName, clientMsg);
                    break;

                case Service.SYSTEM_SERVICE:
                case Service.CHAT_SERVICE:
                    //log.info("chat service");
                    ServerService.processCommand(session, userID, userName, clientMsg);
                    break;

                case Service.PARADISE_ISLAND_SERVICE:
                    serviceParadise.processCommand(session, userID, userName, clientMsg);
                    break;

                case Service.FARM_SERVICE:
                    farmService.processCommand(session, userID, userName, clientMsg);
                    break;
                case Service.FISHING_SERVICE:
                    fishingService.processCommand(session, userID, userName, clientMsg);
                    break;


//                case ItemService.ITEM_FEATURE_SERVICE:
//                    //log.info("chat service");
//                    ItemFeatureService.ProcessCommand(session, userID, userName, clientMsg);
//                    break;
//                case ItemService.GAME_NEWS_SERVICE:
//                    NewsService.ProcessCommand(session, userID, userName, clientMsg);
//                    break;
//                case BinhConstant.BINH_SERVICE:
//                    serviceBinh.processCommand(session, userID, userName, clientMsg);
//                    break;
//                case DominoConstant.DOMINO_SERVICE:
//                    serviceDomino.processCommand(session, userID, userName, clientMsg);
//                    break;
//                case BattleshipConstant.BATTLESHIP_SERVICE:
//                    serviceBattleship.processCommand(session, userID, userName, clientMsg);
//                    break;
//                case PokerConstant.POKER_SERVICE:
//                    servicePoker.processCommand(session, userID, userName, clientMsg);
//                    break;
//                case Service.MESSAGING_SERVICE:
//                    serviceChat.processCommand(session, userID, userName, clientMsg);
//                    break;
                default:
                    session.close(false);
                    break;
            }

        } //        catch (IllegalArgumentException illarg)
        //        {
        //            //log.error(illarg.getMessage());
        //            log.info(illarg.getMessage());
        //        }
        catch (Exception ex) {
            log.error(String.format("ERR INFO: User %s, Cmd %d, Svc %d",
                    userName, actionCommand, svcType));
            log.error("messageReceived", ex);
        }

//        *** HACK ***
//        try
//        {
//            String str = session.getRemoteAddress().toString().replaceAll("/", "");
//            char c;
//            String ip = "";
//            StringBuilder sb = new StringBuilder();
//
//            for(int i = 0; i < str.length(); i++)
//            {
//                c = str.charAt(i);
//                if(c == ':')
//                {
//                    ip = sb.toString();
//                    break;
//                }
//                else
//                {
//                    sb.append(c);
//                }
//            }
//
//            log.info("IP:" + ip);
//        }
//        catch(Exception ex) {}
    }

    @Override
    public void sessionClosed(IoSession session) throws Exception {
        String room = (String) session.getAttribute("room");

        try {
            String user = (String) session.getAttribute("user");
            String tableId = (String) session.getAttribute("table");
            int mapID = (Integer) session.getAttribute("map", -1);
            int userID = (Integer) session.getAttribute("userid", -1);

//            if ((user != null) && (chatRoom != null) && (userID != -1)) {
//                serviceChat.leaveChatRoom(session, user, chatRoom);
//            }

            if ((user != null) && (userID != -1) && (mapID != -1)) {
                GameMapObj gm = (GameMapObj) MapManager.getInstance().getMap(mapID);
                if (gm != null) {
                    serviceParadise.leaveMap(session, user, mapID, true);
                    /*
                     * If Map is Fishing , Leave Slot
                     */
                    if(mapID/10000==2){
                        gm.leaveslot(userID);
                    }
                }
            }

            if ((user != null) && (tableId != null) && (userID != -1)) {

                if (tableId.startsWith("TL")) {
                    GameTable gt = (GameTable) RoomManager.getInstance().getTable(tableId);
                    if (gt != null) {
//                        if (gt.isPlaying && gt.playerJoined.contains(user)) {
//                            long leaveTime = System.currentTimeMillis();
//                            LeaveUserObj leaveObj = new LeaveUserObj(userID, user, leaveTime, tableId);
//                            synchronized (leaveUserQueue) {
//                                log.info("Put user {} to queue", user);
//                                leaveUserQueue.put(user, leaveObj);
//                            }
//                        } else {
                            serviceTienLen.leaveTable(session, user, tableId, true);
//                        }
                    }
                }
            }
            //Anlh add for Binh reconnect
//                if (tableId.startsWith("TB")) {
//                    BinhTable gt = (BinhTable) BinhRoomManager.getInstance().getTable(tableId);
//                    if (gt != null) {
//                        if (gt.isPlaying && gt.playerJoined.contains(user)) {
//                            long leaveTime = System.currentTimeMillis();
//                            LeaveUserObj leaveObj = new LeaveUserObj(userID, user, leaveTime, tableId);
//                            synchronized (leaveUserQueue) {
//                                log.info("Put user {} to queue", user);
//                                leaveUserQueue.put(user, leaveObj);
//                            }
//                        } else {
//                            serviceBinh.leaveTable(session, user, tableId, true);
//                        }
//                    }
//                }

//                if (tableId.startsWith("DO")) {
//                    DominoTable gt = (DominoTable) DominoRoomManager.getInstance().getTable(tableId);
//                    if (gt != null) {
//                        if (gt.isPlaying && gt.playerJoined.contains(user)) {
//                            long leaveTime = System.currentTimeMillis();
//                            LeaveUserObj leaveObj = new LeaveUserObj(userID, user, leaveTime, tableId);
//                            synchronized (leaveUserQueue) {
//                                log.info("Put user {} to queue", user);
//                                leaveUserQueue.put(user, leaveObj);
//                            }
//                        } else {
//                            serviceDomino.leaveTable(session, user, tableId, true);
//                        }
//                    }
//                }
//
//                if (tableId.startsWith("BS")) {
//                    BattleshipTable gt = (BattleshipTable) BattleshipRoomManager.getInstance().getTable(tableId);
//                    if (gt != null) {
//                        if (gt.isPlaying && gt.playerJoined.contains(user)) {
//                            long leaveTime = System.currentTimeMillis();
//                            LeaveUserObj leaveObj = new LeaveUserObj(userID, user, leaveTime, tableId);
//                            synchronized (leaveUserQueue) {
//                                log.info("Put user {} to queue", user);
//                                leaveUserQueue.put(user, leaveObj);
//                            }
//                        } else {
//                            serviceBattleship.leaveTable(session, user, tableId, true);
//                        }
//                    }
//                }
//
//                if (tableId.startsWith("PK")) {
//                    PokerTable gt = (PokerTable) PokerRoomManager.getInstance().getTable(tableId);
//                    if (gt != null) {
//                        if (gt.isPlaying && gt.playerJoined.contains(user)) {
//                            long leaveTime = System.currentTimeMillis();
//                            LeaveUserObj leaveObj = new LeaveUserObj(userID, user, leaveTime, tableId);
//                            synchronized (leaveUserQueue) {
//                                log.info("Put user {} to queue", user);
//                                leaveUserQueue.put(user, leaveObj);
//                            }
//                        } else {
//                            servicePoker.leaveTable(session, user, tableId, true);
//                        }
//                    }
//                }


            try {
//                    serviceTienLen.leaveTable(session, user, tableId, true);
                VCoreUserDataProvider.getInstance().UpdateActiontime(userID, 1);
            } catch (Exception ex) {
                log.error("sessionClosed", ex);
            }
//            }

//            synchronized (users)
//            {
//                users.remove(user);
//            }
            log.info("Remove user {} (session {}) from system", user, session.getId());
//            synchronized (sessions) {
//                sessions.remove(session);
//            }
            clientSessions.remove(userID);

            // Disconnect to VMServer if has connection in BridgeService
            IoSession ioSession = BridgeService.connectionList.get(userID);

            if (ioSession != null) {
                if (ioSession.isConnected()) {
                    ioSession.close(false);
                }

                BridgeService.connectionList.remove(userID);
            }
        } catch (Exception ex) {
            log.error("sessionClosed", ex);
        }
    }

    private int getOnlineUserCount() {
//        return sessions.size();
        return clientSessions.size();
    }

    public static void sessionWrite(IoSession session, String message) {
        //log.info("Sending: " + message);
        // Run the custom logic
//        if(VMServer.runCustomLogic)
//            message = objCustomLogicClass.customLogic(message);
        //session.write(Base64Coder.encodeString(message));
        //session.write(message);
    }

//    private void updateUserID() {
//        MySQLCommonDataProvider commonDataManager = DbManager.GetCommonDataManager();
//        //commonDataManager.updateUserID();
//    }
    public static IoSession getSessionByUserName(String userName) {
        //TODO:Exception in thread "Timer-300878" java.util.ConcurrentModificationException
//        synchronized (VMSProtocolHandler.clientSessions) {
            Iterator<IoSession> iter = VMSProtocolHandler.clientSessions.values().iterator();
            while (iter.hasNext()) {
                IoSession s = (IoSession) iter.next();
                if (userName.equals(s.getAttribute("user"))) {
                    return s;
                }
            }
//        }
//        log.error("Cannot identify session for {} - {}",userName,
//                Thread.currentThread().getStackTrace().toString());
        return null;
    }

    public static IoSession getSessionByUserID(int userID) {
        // fix java.util.ConcurrentModificationException
//        synchronized (VMSProtocolHandler.sessions) {
//            Iterator<IoSession> iter = VMSProtocolHandler.sessions.iterator();
//            while (iter.hasNext()) {
//                IoSession s = (IoSession) iter.next();
//                if (userID == (Integer) s.getAttribute("userid")) {
//                    return s;
//                }
//            }
//        }
        if (clientSessions.containsKey(userID)) {
            return clientSessions.get(userID);
        };
        log.error("Cannot identify session for {} - {}", userID,
                Thread.currentThread().getStackTrace().toString());
        return null;
    }

    private void updateUserID() {
        MySQLCommonDataProvider dataManager = DbManager.GetCommonDataManager();
        dataManager.updateUserID();
        dataManager.closeConnection();
    }

    public static LeaveUserObj getLeaveUserObjByUserName(String userName) {
        if (leaveUserQueue != null && !leaveUserQueue.isEmpty()) {
            synchronized (leaveUserQueue) {
                return leaveUserQueue.get(userName);
            }
        }
        return null;
    }
}
